package com.linkallcloud.web.processor;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.linkallcloud.core.castor.Castors;
import com.linkallcloud.core.dto.CheckResult;
import com.linkallcloud.core.dto.Trace;
import com.linkallcloud.core.exception.BizException;
import com.linkallcloud.core.face.message.FaceMessage;
import com.linkallcloud.core.face.message.request.FaceRequest;
import com.linkallcloud.core.lang.Mirror;
import com.linkallcloud.core.lang.Strings;
import com.linkallcloud.core.log.Log;
import com.linkallcloud.core.log.Logs;
import com.linkallcloud.core.util.HibernateValidator;
import com.linkallcloud.core.www.ISimpleUser;
import com.linkallcloud.core.www.utils.InputStreamUtils;
import com.linkallcloud.web.utils.Controllers;

public abstract class BaseProcessor {
    protected static Log log = Logs.get();

    private static final String NO_METHOD_STUFF = "_NO";
    protected Map<String, Method> checkMethods = new HashMap<>();

    protected Method getCheckMethod(JoinPoint joinPoint, String methodName, boolean user) {
        String checkMethodName = methodName + "Check";
        String checkMethodKey = getCheckMethodCacheKey(joinPoint.getTarget().getClass(), checkMethodName);
        if (checkMethods.containsKey(checkMethodKey)) {
            return checkMethods.get(checkMethodKey);
        } else {
            // 检查缓存标记
            String noCheckMethodKey =
                    getCheckMethodCacheKey(joinPoint.getTarget().getClass(), checkMethodName + NO_METHOD_STUFF);
            if (checkMethods.containsKey(noCheckMethodKey)) {
                return getDefaultCheckMethod(joinPoint, user);
            } else {
                Method m = getCheckMethodByName(joinPoint, checkMethodName, user);
                if (m != null) {
                    checkMethods.put(checkMethodKey, m);
                    return m;
                } else {
                    // 缓存标记
                    checkMethods.put(noCheckMethodKey, null);
                    return getDefaultCheckMethod(joinPoint, user);
                }
            }
        }
    }

    protected Method getDefaultCheckMethod(JoinPoint joinPoint, boolean user) {
        String checkMethodKey = getCheckMethodCacheKey(joinPoint.getTarget().getClass(), "check");
        if (checkMethods.containsKey(checkMethodKey)) {
            return checkMethods.get(checkMethodKey);
        } else {
            Method m = getCheckMethodByName(joinPoint, "check", user);
            checkMethods.put(checkMethodKey, m);
            return m;
        }
    }

    protected Method getCheckMethodByName(JoinPoint joinPoint, String checkMethodName, boolean user) {
        Object target = joinPoint.getTarget();
        try {
            Class<?>[] args = null;
            if (user) {
                args = new Class[] { Trace.class, String.class, FaceRequest.class, ISimpleUser.class };
            } else {
                args = new Class[] { Trace.class, String.class, FaceRequest.class };
            }
            return target.getClass().getMethod(checkMethodName, args);
        } catch (NoSuchMethodException e) {
        } catch (SecurityException e) {
        }
        return null;
    }

    private String getCheckMethodCacheKey(Class<?> clazz, String key) {
        return clazz.getName() + "-" + key;
    }

    public Object process(ProceedingJoinPoint joinPoint, Method method, boolean check, boolean faceUser)
            throws Throwable {
        HttpServletRequest hsr = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        log.debug("接收到的请求URL：" + hsr.getRequestURL());

        String messagePkg = null;
        try {
            messagePkg = InputStreamUtils.convertStreamToString(hsr.getInputStream());
            log.debug("接收到的数据包：" + messagePkg);
            if (Strings.isBlank(messagePkg)) {
                log.error("400003：从Request读取InputStream，结果为空。");
                throw new BizException("400003", "从Request读取InputStream，结果为空。");
            }
        } catch (Throwable e) {
            log.error("400002：从Request读取InputStream出错", e);
            throw new BizException("400002", "从Request读取InputStream出错");
        }

        // Class<?>[] argTypes = method.getParameterTypes();
        Type[] argTypes = method.getGenericParameterTypes();
        Object[] args = joinPoint.getArgs();

        FaceMessage faceRequest = null;
        ISimpleUser suser = null;
        int suserIdx = -1;
        int tIdx = -1;
        Trace t = new Trace(true);
        int checkIndex = -1;
        Object checkResult = null;
        if (argTypes != null && argTypes.length > 0) {
            for (int i = 0; i < argTypes.length; i++) {
                Mirror<?> argMirror = Mirror.me(argTypes[i]);
                if (argMirror.isOf(FaceMessage.class)) {
                    Type thisArgType = Controllers.parseConcreteArgType(joinPoint, argTypes[i]);
                    faceRequest = doConvert2RealRequest(messagePkg, thisArgType, method);
                    if (faceRequest instanceof FaceRequest) {
                        FaceRequest req = (FaceRequest) faceRequest;
                        if (req.getT() == null) {
                            req.setT(t);
                        } else {
                            t = req.getT();
                        }
                        t.setAppKey(req.getAppCode());
                    }
                    beanValidator(faceRequest);
                    args[i] = faceRequest;
                } else if (argMirror.isOf(ISimpleUser.class)) {
                    suserIdx = i;
                } else if (argMirror.isOf(Trace.class)) {
                    tIdx = i;
                } else if (argMirror.isOf(CheckResult.class)) {
                    checkIndex = i;
                } else if (argMirror.isOf(HttpServletRequest.class)) {
                    args[i] = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                } else if (argMirror.isOf(HttpServletResponse.class)) {
                    args[i] = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                }
            }

            if (faceUser) {// 需要登录校验
                suser = doCheckLogin(faceRequest, method);
                t.traceUser(suser);
            }

            if (check) {
                Method cm = getCheckMethod(joinPoint, method.getName(), faceUser);
                if (cm != null) {
                    if (faceUser) {
                        checkResult = cm.invoke(joinPoint.getTarget(),
                                new Object[] { t, method.getName(), faceRequest, suser });
                    } else {
                        checkResult =
                                cm.invoke(joinPoint.getTarget(), new Object[] { t, method.getName(), faceRequest });
                    }
                }
            }

            if (suser != null && suserIdx != -1) {
                args[suserIdx] = suser;
            }
            if (tIdx != -1) {
                args[tIdx] = t;
            }
            if (checkIndex != -1) {
                args[checkIndex] = checkResult;
            }
        }
        return doProcess(joinPoint, method, args);
    }

    protected FaceMessage doConvert2RealRequest(String messagePkg, Type type, Method method) throws Exception {
        return Castors.me().castTo(messagePkg, FaceMessage.class);
    }

    protected ISimpleUser doCheckLogin(FaceMessage faceRequest, Method method) throws Exception {
        return null;
    }

    protected void beanValidator(Object object) {
        if (object != null) {
            HibernateValidator.getInstance().validatorThrowException(object);
        }
    }

    protected Object doProcess(ProceedingJoinPoint joinPoint, Method method, Object[] args) throws Throwable {
        return joinPoint.proceed(args);
    }

}
